package com.daffodil.util.sm;

import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.daffodil.core.entity.SM2Key;

/**
 * 国密sm2 级别 对应RSA
 * 
 * @author yweijian
 * @date 2020年12月16日
 * @version 1.0
 * @description
 */
public class SM2Utils {

	private static final Logger log = LoggerFactory.getLogger(SM2Utils.class);

	/**
	 * 生成公私钥对
	 * 
	 * @param compressedPubKey 是否压缩公钥
	 * @return
	 */
	public static SM2Key genSM2Key(boolean compressedPubKey) {
		AsymmetricCipherKeyPair asymmetricCipherKeyPair = genKeyPair0();

		// 提取公钥点
		ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();
		// 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥,04的时候,可以去掉前面的04
		String pubKey = Hex.toHexString(ecPoint.getEncoded(compressedPubKey));

		BigInteger privatekey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();
		String priKey = privatekey.toString(16);

		SM2Key sm2Key = new SM2Key(priKey, pubKey);
		return sm2Key;
	}

	/**
	 * SM2加密算法
	 * 
	 * @param publicKey 公钥
	 * @param data      数据
	 * @return
	 */
	public static String encryptData(String publicKey, String data) {
		// 获取一条SM2曲线参数
		X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
		// 构造domain参数
		ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(),
				sm2ECParameters.getN());
		// 提取公钥点
		ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(CodeUtils.hexStringToBytes(publicKey));
		// 公钥前面的02或者03表示是压缩公钥，04表示未压缩公钥, 04的时候，可以去掉前面的04
		ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);

		SM2Engine sm2Engine = new SM2Engine();
		sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));

		byte[] arrayOfBytes = null;
		try {
			byte[] in = data.getBytes("utf-8");
			arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
		} catch (Exception e) {
			log.error("SM2加密失败");
		}
		return Hex.toHexString(arrayOfBytes);
	}

	/**
	 * SM2加密算法
	 * 
	 * @param publicKey 公钥
	 * @param data      明文数据
	 * @return
	 */
	public static String encrypt(PublicKey publicKey, String data) {
		ECPublicKeyParameters ecPublicKeyParameters = null;
		if (publicKey instanceof BCECPublicKey) {
			BCECPublicKey bcecPublicKey = (BCECPublicKey) publicKey;
			ECParameterSpec ecParameterSpec = bcecPublicKey.getParameters();
			ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(), ecParameterSpec.getG(), ecParameterSpec.getN());
			ecPublicKeyParameters = new ECPublicKeyParameters(bcecPublicKey.getQ(), ecDomainParameters);
		}

		SM2Engine sm2Engine = new SM2Engine();
		sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));

		byte[] arrayOfBytes = null;
		try {
			byte[] in = data.getBytes("utf-8");
			arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
		} catch (Exception e) {
			log.error("SM2加密失败");
		}
		return Hex.toHexString(arrayOfBytes);
	}

	/**
	 * SM2解密算法
	 * 
	 * @param privateKey 私钥
	 * @param cipherData 密文数据
	 * @return
	 */
	public static String decryptData(String privateKey, String cipherData) {
		try {
			byte[] cipherDataByte = Hex.decode(cipherData);

			// 获取一条SM2曲线参数
			X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
			// 构造domain参数
			ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());

			BigInteger privateKeyD = new BigInteger(privateKey, 16);
			ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);

			SM2Engine sm2Engine = new SM2Engine();
			sm2Engine.init(false, privateKeyParameters);
			byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);

			return new String(arrayOfBytes, "utf-8");
		} catch (Exception e) {
			log.error("SM2解密失败");
		}
		return null;
	}

	/**
	 * SM2解密算法
	 * 
	 * @param privateKey 私钥
	 * @param cipherData 密文数据
	 * @return
	 */
	public static String decrypt(PrivateKey privateKey, String cipherData) {
		byte[] cipherDataByte = Hex.decode(cipherData);

		BCECPrivateKey bcecPrivateKey = (BCECPrivateKey) privateKey;
		ECParameterSpec ecParameterSpec = bcecPrivateKey.getParameters();

		ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(), ecParameterSpec.getG(), ecParameterSpec.getN());

		ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(bcecPrivateKey.getD(), ecDomainParameters);

		SM2Engine sm2Engine = new SM2Engine();
		sm2Engine.init(false, ecPrivateKeyParameters);

		try {
			byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
			return new String(arrayOfBytes, "utf-8");
		} catch (Exception e) {
			log.error("SM2解密失败");
		}
		return null;
	}

	/**
	 * 生成SM2公私钥对
	 * 
	 * @return
	 */
	private static AsymmetricCipherKeyPair genKeyPair0() {
		// 获取一条SM2曲线参数
		X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");

		// 构造domain参数
		ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());

		// 1.创建密钥生成器
		ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();

		// 2.初始化生成器,带上随机数
		try {
			keyPairGenerator .init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}

		// 3.生成密钥对
		AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
		return asymmetricCipherKeyPair;
	}

}
